package com.google.code.coma.space.implementation;

import com.google.code.coma.space.CallContext;
import com.google.code.coma.space.SelectorCallback;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozartspaces.capi3.*;
import org.mozartspaces.capi3.javanative.coordination.NativeCoordinator;
import org.mozartspaces.capi3.javanative.coordination.NativeSelector;
import org.mozartspaces.capi3.javanative.isolation.NativeIsolationManager;
import org.mozartspaces.capi3.javanative.isolation.NativeSubTransaction;
import org.mozartspaces.capi3.javanative.operation.NativeEntry;

import java.util.List;

/**
 * @author Dominik Strasser, dominikstr@gmail.com
 */
public class SpaceSelectorImpl implements NativeSelector {
	private static final Log log = LogFactory.getLog(SpaceSelectorImpl.class);

	private ComaFIFOCoordinatorImpl coordinator;

	private NativeIsolationManager isolationManager;

	private SelectorCallback callback;

	private static final Object monitor = new Object();

	public SpaceSelectorImpl(SelectorCallback callback) {
		this.callback = callback;
	}

	@Override
	public List<NativeEntry> getAll(IsolationLevel isolationLevel, NativeSubTransaction stx, OperationType opType) throws CountNotMetException, EntryLockedException {
		try {
			return callback.doInTransaction(coordinator.queue, new DefaultCallContext(isolationLevel, stx, opType, isolationManager));
		} catch (Capi3Exception e) {
			//Exception occured. We have to check the type of the excpetion because some are okay, and for the others
			//we have to rollback the transaction.
			if (e instanceof CountNotMetException) {
				if (log.isDebugEnabled()) {
					log.debug("Blocking " + opType.name() + " call");
				}
				throw (CountNotMetException) e;
			} else if (e instanceof EntryLockedException) {
				EntryLockedException ex = (EntryLockedException) e;
				if (log.isDebugEnabled()) {
					log.debug("Entry is locked by transaction " + ex.getTxId());
				}
				throw ex;
			}
			log.error("Exception in callback handler", e);
			try {
				if (log.isDebugEnabled()) {
					log.debug("Exception in callback handler. Abort transaction");
				}
				//rollback the transaction
				stx.rollback();
			} catch (InvalidTransactionException e1) {
				log.error("Error in transaction rollback", e1);
			}
		}
		return null;
	}

	@Override
	public NativeEntry getNext(IsolationLevel isolationLevel, NativeSubTransaction stx, OperationType opType) throws CountNotMetException, EntryLockedException {
		return null;
	}

	@Override
	public void setPredecessor(NativeSelector next) {

	}

	@Override
	public void link(NativeCoordinator coordinator, NativeIsolationManager isolationManager) {
		if (coordinator instanceof ComaFIFOCoordinatorImpl) {
			this.coordinator = (ComaFIFOCoordinatorImpl) coordinator;
			this.isolationManager = isolationManager;
			if (log.isDebugEnabled()) {
				log.debug("Linked " + coordinator.getName() + " to selector");
			}
		}
	}

	@Override
	public String getName() {
		return ComaFIFOCoordinatorImpl.name;
	}

	@Override
	public int getCount() {
		return 0;
	}

	private class DefaultCallContext implements CallContext {
		private IsolationLevel isolationLevel;

		private NativeSubTransaction nativeSubTransaction;

		private OperationType operationType;

		private NativeIsolationManager nativeIsolationManager;

		private DefaultCallContext(IsolationLevel isolationLevel, NativeSubTransaction nativeSubTransaction, OperationType operationType, NativeIsolationManager nativeIsolationManager) {
			this.isolationLevel = isolationLevel;
			this.nativeSubTransaction = nativeSubTransaction;
			this.operationType = operationType;
			this.nativeIsolationManager = nativeIsolationManager;
		}

		@Override
		public IsolationLevel getIsolationLevel() {
			return isolationLevel;
		}

		@Override
		public NativeSubTransaction getTransaction() {
			return nativeSubTransaction;
		}

		@Override
		public OperationType getOperation() {
			return operationType;
		}

		@Override
		public NativeIsolationManager getIsolationManager() {
			return nativeIsolationManager;
		}
	}
}
